from datetime import datetime
from typing import List, Optional, Union

from pydantic import BaseModel, field_validator, ValidationError, Field

from .const import DEFAULT_PAGE_ID, DEFAULT_PAGE_SIZE

'''
字段设置原则：
- 字符串, 非必填, 默认空字符串, 不要设置为None, 避免json返回出现null
- 列表类型, 默认[], 不要设置为None, 避免json返回出现null

response命名原则：所有用于应答返回的结构体定义，皆用Response开头

请求体命名原则：如果需要额外定义post body结构体, 皆用PostBody开头
'''


'''
here for Definition of Organization-related data structures
'''
class Organization(BaseModel):
    '''atom table, 机构表'''
    id: Optional[int] = Field(default=0, serialization_alias="organization_id")  #
    name: str = Field(max_length=64, serialization_alias="organization_name")  # 别名用于data/info接口序列化
    desc: Optional[str] = ""  # 机构描述，非必填
    domain: Optional[str] = ""  # 机构对外可访问域名，用于访问agent节点的信息, 如果设置必须http://或https://格式
    create_time: datetime | None = None
    update_time: datetime | None = None

    # TODO 需要校验是否http:// 或https://格式
    @field_validator('domain')
    @classmethod
    def domain_url_check(cls, v: str):
        if not v:
            return ""
        # else:
        #    v.lstrip().find("http")  #待写一个url格式的校验方法
        return v


'''
here for Definition of Node-related data structures
'''
class Node(BaseModel):
    '''atom table, 节点表'''
    id: Optional[int] = Field(default=0, serialization_alias="node_id")
    ip: str = Field(min_length=7, max_length=15)  # IP长度设置：0.0.0.0  255.255.255.255
    organization_id: int
    hostname: Optional[str] = ""
    os: Optional[str] = ""
    cpu_cores: int = 0
    cpu_name: Optional[str] = ""  # cat /proc/cpuinfo, Intel(R) Xeon(R) Platinum 8358 CPU @ 2.60GHz
    cpu_memory: int = 0  # unit: GB
    gpu_cores: int = 0
    gpu_name: Optional[str] = ""  # nvidia-smi, NVIDIA A40
    gpu_memory: int = 0  # unit: GB
    create_time: datetime = None
    refresh_time: Union[datetime, None] = None
    update_time: datetime = None


class ItemOrganization(Organization, Node):
    '''联合机构表和节点表的详情信息，并增加节点存储计算资源详情'''
    details: Optional[str] = ""  # 存储计算资源详情

class ResponseOrganizationInfo(BaseModel):
    count_total: int = 0
    page_id: int = DEFAULT_PAGE_ID  # 当前分页码
    page_total: int = 0  # 总分页数
    page_size: int = DEFAULT_PAGE_SIZE  # 默认每页显示的条数
    info: Optional[List[ItemOrganization]] = []


'''
here for Definition of DataSource-related data structures
'''
class DataSource(BaseModel):
    '''atom table, 数据源表'''
    id: Optional[int] = Field(default=0, serialization_alias="data_id")
    ip: str = Field(min_length=7, max_length=15)  # IP长度设置：0.0.0.0  255.255.255.255
    organization_id: int
    name: Optional[str] = ""
    desc: Optional[str] = ""
    data_path: Optional[str] = ""
    create_time: datetime = None
    update_time: datetime = None


'''
here for Definition of Model-related data structures
'''
class Model(BaseModel):
    '''atom table, 算法模型表'''
    id: Optional[int] = Field(default=0, serialization_alias="model_id")
    # 模型类型，分为：0=联邦训练用模型/1=密态分析用模型，默认0
    type: Optional[int] = Field(default=0)
    name: Optional[str] = Field(max_length=64)
    desc: Optional[str] = ""
    create_time: datetime = None
    update_time: datetime = None


'''
here for Definition of TrainTask-related data structures
'''
class TrainTask(BaseModel):
    '''atom table, 任务训练表'''
    id: Optional[int] = Field(default=0, serialization_alias="task_id")
    name: Optional[str] = Field(max_length=64, serialization_alias="task_name") # required
    # status任务状态：unstart未开始/training训练中/finished训练完成/error训练过程任意错误的情况
    status: Optional[str] = Field(default="unstart", serialization_alias="task_status")
    # arch训练方式：0=横向/1=纵向
    arch: Optional[int] = Field(default=0)      # TODO arch取值仅为0和1、2，如何限制字段传参
    # topology 训练的网络拓扑架构：0=中心式/1=对等网络，目前仅支持中心式
    topology: Optional[int] = Field(default=0)  # TODO arch取值仅为0和1，如何限制字段传参
    server_id: Optional[int] = 0            # 中心式训练方式的服务端节点id，负责模型聚合和分发
    client_ids: Optional[List[int]] = []    # 参与训练的客户端节点id列表
    data_ids: Optional[List[int]] = []      # 与client一一对应，训练节点所需要的数据id列表
    label_id: Optional[int] = 0             # 存储标签数据的节点id
    model_id: Optional[int] = 0             # 训练算法标识id
    start_time: Union[datetime, None] = None  # 任务训练开始时间
    finish_time: Union[datetime, None] = None  # 任务训练完成时间
    create_time: Union[datetime, None] = None
    update_time: Union[datetime, None] = None
    image: Union[dict,None] = None


class ItemTrainTask(TrainTask):
    # organizations：表示训练参与的是多个机构，展示两个客户端的机构名称，暂定用"|"分隔
    # 例如：clientA.name|clientB.name，客户端只负责展示，由服务端决定拼装方式
    organizations: Optional[str] = ""
    status_info: Optional[dict] = {"total_round":0, "current_round":0} # 未开始的状态


class ResponseTrainTaskInfo(BaseModel):
    '''用于task/info获取任务列表的接口响应'''
    count_total: int = 0
    page_id: int = DEFAULT_PAGE_ID      # 当前分页码
    page_total: int = 0                 # 总分页数
    page_size: int = DEFAULT_PAGE_SIZE  # 默认每页显示的条数
    info: Optional[List[ItemTrainTask]] = []


class ResponseTrainTaskDetail(BaseModel):
    '''用于task/query任务详情的接口响应'''
    id: int = Field(default=0, serialization_alias="task_id")
    name: str           # 任务名
    arch: int           # 训练方式 0=横向训练/1=纵向训练
    topology: int       # 训练机构：0=中心式/1=对等网络
    server: str         # server详情，由服务端特殊处理，展示为:机构名/ip，标明是某个机构下的节点作为主节点
    clients: List[str]  # client详情，字段拼装同上，展示为:机构名/ip，标明是某个机构下的节点参与训练    
    label: str          # label标签节点详情，字段拼装同上:机构名/ip
    data: List[str]     # 与clients一一对应，展示：机构名/数据名，或直接：数据名，与前端商量看展示效果来决定
    model: str          # 模型名， model.name
    start_time: Union[datetime,None] = None     # 训练开始时间
    finish_time: Union[datetime,None] = None    # 训练结束时间
    image: Union[dict,None] = None

class ResponseTrainTaskStatus(BaseModel):
    '''用于task/query任务详情的接口响应'''
    id: int
    status: str = ''
    detail_info: dict = {}


class AnalysisTask(BaseModel):
    id: Optional[int] = Field(default=0, serialization_alias="analysis_id")


class ItemOrganization(Organization, Node):
    '''联合机构表和节点表的详情信息，并增加节点存储计算资源详情'''
    details: Optional[str] = ""  # 存储计算资源详情


class ItemData(BaseModel):
    id: Optional[int] = Field(default=0)  #
    organization: str = Field(max_length=64, serialization_alias="organization_name")
    ip: str = Field(min_length=7, max_length=15)  # IP长度设置：0.0.0.0  255.255.255.255
    name: Union[str, None]
    desc: Union[str, None]
    data_path: str = ""
    create_time: datetime = None
    update_time: datetime = None


# TODO  请先定义datasource表结构，再通过继承方式组装ItemData，这样有data_source原子表可以用
class ItemData1(DataSource):
    organization: str = Field(max_length=64, serialization_alias="organization_name")


class ItemSelectData(BaseModel):
    id: Optional[int] = Field(default=0)  #
    organization: str = ""
    ip: str = Field(min_length=7, max_length=15)  # IP长度设置：0.0.0.0  255.255.255.255
    name: str = ""


'''
所有用于应答返回的结构体定义，皆用Response开头
'''


class ResponseOrganizationInfo(BaseModel):
    count_total: int = 0
    page_id: int = DEFAULT_PAGE_ID  # 当前分页码
    page_total: int = 0  # 总分页数
    page_size: int = DEFAULT_PAGE_SIZE  # 默认每页显示的条数
    info: Optional[List[ItemOrganization]] = []


class ResponseAddLastID(BaseModel):
    """所有Add操作，新增的唯一标识id，皆用此结构"""
    id: int


class ResponseDataInfo(BaseModel):
    count_total: int = 0
    page_id: int = DEFAULT_PAGE_ID  # 当前分页码
    page_total: int = 0  # 总分页数
    page_size: int = DEFAULT_PAGE_SIZE  # 默认每页显示的条数
    info: Optional[List[ItemData]] = []


class ResponseDataSelectInfo(BaseModel):
    info: Optional[List[ItemData]] = []


class ResponseDataCard(BaseModel):
    readme: str = ""
    image_name: str = ""


class ResponseDataViewer(BaseModel):
    field: list = []  # TODO 命名错误，field字段名
    value: list = []
    total:int




'''
如果需要额外定义post body结构体, 皆用PostBody开头
'''
class PostBodyNodeRefresh(BaseModel):
    ip: str = Field(min_length=7, max_length=15)
    organization_id: int
    domain: Optional[str] = ""






class AnalysisTask(BaseModel):
    '''目前的智能分析，不需要建原子表来存储'''
    id: Optional[int]  = Field(default=0, serialization_alias="analysis_id")


class PostBodyAnalysisSearch(BaseModel):
    script: str = Field(max_length=2048) 
    data_id: Optional[int] = 0        # 数据源id

class PostBodyAnalysisExecute(BaseModel):
    script: str = Field(max_length=20480) 
    algorithm: Optional[str]         # 密态分析算法名

class Image(BaseModel):
    name: str                   # 图片名称
    # 注：这里图片会进行特殊处理，给出图片的路径，业务端需要通过analysis/image图片接口重新查询
    url: Optional[str] = ""     # 图片地址
    
class ResponseDataVisual(BaseModel):
    is_image: bool = Field(default=True)  # 是否可视化为图片
    image: Union[Image,None]  # 图片内容

class ResponseAsyncExecuteID(BaseModel):
    '''用于调起异步执行的接口，统一返回job_id
    '''
    job_id: Optional[str]

class ResponseAnalysisSearch(ResponseDataViewer):
    total: int      # 增加返回本次搜索的总条数

    
class ResponseAsyncExecuteStatus(BaseModel):
    '''用于查询异步执行的状态
    '''
    job_id: Optional[str]   # 分析任务名，由backend服务拼装，规则为：action_token_
    type: Optional[str]     # 任务类型，search=加密搜索，analysis=密态分析      
    token: Optional[str]    # 任务标识，search任务格式：机构名/ip/data_name/data_id，analysis任务格式：密态分析算法名
    # status异步任务状态：unstart未开始/running运行中/finished运行完成/error运行过程任意错误的情况
    status: Optional[str] = Field(default="unstart")
    runtime: Optional[int] = 0  # 任务运行时间
    start_time: Optional[datetime] = None         # 异步指令执行的开始时间
    finish_time: Optional[datetime] = None        # 异步指令执行的结束时间